fix(cli): stabilize thinking block height, replace transcript overlay with inline Ctrl+O toggle - #8077
Conversation
|
Thanks for the PR — and apologies, my earlier stage comments here were stale (they reviewed a much smaller intermediate push). Re-running the gate against the current head. Template: complete now ✓ — all the required sections are present (what/why, reviewer test plan with before/after tmux captures, tested-on table, risk & scope, linked issues, Chinese translation). Problem: observed, not theoretical. The streaming thinking block's height flicker is real — empty lines and list markers in the reasoning output made the block jump between 1–5 rows on every chunk, pushing everything below it around. The grow-only tracker from #7397 mitigated but didn't remove it. Maintainer @wenshao independently reproduced and measured this in a real terminal (comment 5143375337): 3 height changes before vs. 0 after, across 50 frames. Direction: aligned, and already settled by the maintainer. Default-collapsing the streaming preview is the right call, and replacing the full-screen transcript overlay with an inline Ctrl+O full-detail toggle matches Claude Code's model. @wenshao reviewed the direction in depth and approved at this head. CHANGELOG shows active work in this area (#7397, #7866). Size: this grew well past the original flicker fix. It now removes the entire Approach: the scope is right for what it set out to do — every removal is a direct consequence of deleting the overlay, no drive-by churn. The sub-agent panel ( Risk: no elevated risk signals — none of the changed files match the revert-correlated high-risk paths. Moving on to code review. 🔍 中文说明感谢贡献——抱歉,我此前在本线程的阶段评论已过期(它们审查的是一个更小的中间推送)。现针对当前 head 重新运行 gate。 模板:现已完整 ✓——所有必需段落齐全(做了什么/为什么、含 before/after tmux 截图的审查测试计划、测试平台表、风险与范围、关联 issue、中文翻译)。 问题:已观测,非理论性。流式思考块的高度闪烁是真实的——推理输出中的空行与列表标记使块在每个 chunk 间跳动 1–5 行,推动下方所有内容。#7397 的只增不减追踪器缓解但未消除。维护者 @wenshao 在真实终端中独立复现并测量了此现象(评论 5143375337):50 帧内修改前 3 次高度变化 vs 修改后 0 次。 方向:对齐,且已由维护者定夺。默认折叠流式预览是正确选择;用 inline Ctrl+O 全详情切换取代全屏 transcript 覆盖层,对齐 Claude Code 模型。@wenshao 深入审查了方向并在本 head 上批准。CHANGELOG 显示该领域有活跃工作(#7397、#7866)。 规模:本 PR 已远超最初的闪烁修复。它现在移除了整个 方案:就其目标而言范围合理——每处移除都是删除覆盖层的直接结果,无顺手改动。子代理面板( 风险:无升级风险信号——改动文件均未命中与回退相关的高风险路径。 进入代码审查 🔍 — Qwen Code · qwen3.8-max-preview Reviewed at |
Code ReviewIndependent proposal: for "the streaming thinking block flickers because its tail-preview height varies", the strictly simpler fix is to render no body at all while collapsed — just the 1-line header — and delete the tail-window + grow-only height tracker entirely. For "Ctrl+O should toggle detail in place like Claude Code instead of opening an overlay", rebind Ctrl+O onto the existing thought-expansion toggle, widen that toggle into an app-wide Comparison with the diff: the PR does exactly this, and cleanly. Every removal is a direct consequence of deleting the overlay; there is no drive-by churn.
On the two findings @wenshao raised before merge — both are addressed at this head:
Leftover (non-blocking, @wenshao's item 3): the No critical blockers. No convention violations. Net ~900 lines removed. Files changed (14 of 35 shown)
CI Test EvidenceCI is fully settled and green on the reviewed head. The ubuntu unit suite (which includes typecheck) passed, so the excised symbols have no dangling consumers; macOS / Windows tests and the no-sandbox integration suite are gated / skipped, consistent with this repo's setup. Integration Tests (CLI, No Sandbox) did not run — noted, not a regression signal for a UI-only change.
One row per check name (latest run); skipped checks noted; failures sort first. / 每个检查名一行(取最新一次运行),标注 skipped,失败项排在最前。 The unit suite pins the render contract (collapsed = no body, expanded = full body; context → fullDetail wiring) but cannot pin the streaming flicker behaviour in a real terminal. That gap was closed manually: maintainer @wenshao drove a real build through a 120×40 tmux pane with a streaming mock provider and measured 0 height changes across 50 frames after vs. 3 before (comment 5143375337), and confirmed Ctrl+O expands thinking and tool groups inline with scroll position preserved. For an independent automated re-confirmation, sandboxed verification would settle this: 中文说明代码审查独立方案: 对于"流式思考块因尾部预览高度变化而闪烁",严格更简的修复是折叠时完全不渲染内容体——只保留 1 行标题——并整体删除尾部窗口与只增不减高度追踪器。对于"Ctrl+O 应像 Claude Code 一样原地切换详情而非打开覆盖层",将 Ctrl+O 重新绑定到既有的思考展开切换,把该切换扩展为应用级 与 diff 对比: PR 完全照此实现,且干净。每处移除都是删除覆盖层的直接结果,无顺手改动。
关于 @wenshao 合入前提出的两项发现——本 head 均已处理:
残留(非阻塞,@wenshao 第 3 项):9 个 locale 文件中 无关键阻塞,无规范违反。净删除约 900 行。 CI 测试证据CI 在受审 head 上已完全 settle 且全绿。ubuntu 单元套件(含 typecheck)通过,故被切除的符号无残留消费者;macOS / Windows 测试与无沙箱集成套件按本仓库设置 gated / skipped。Integration Tests (CLI, No Sandbox) 未运行——记录在案,对纯 UI 改动非回归信号。 单元套件固定了渲染契约(折叠=无内容体、展开=完整内容;context → fullDetail 接线),但无法固定真实终端中的流式闪烁行为。该缺口已由人工闭合:维护者 @wenshao 用流式 mock provider 在 120×40 tmux 中驱动真实构建,测得修改后 50 帧 0 次高度变化 vs 修改前 3 次,并确认 Ctrl+O 内联展开思考与工具组且滚动位置保持。如需独立的自动化复确认,沙箱验证可确认此 PR 的核心声明: — Qwen Code · qwen3.8-max-preview Reviewed at |
|
Confidence: 4/5 — clean, net-negative-code change that takes the strictly simpler path; the maintainer has reviewed it in depth and approved at this head. Non-blocking nits below. Stepping back: this is the right solution to a real, measured problem. The grow-only tracker from #7397 was fighting the layout — rendering variable-height content during streaming can never be padded stable, and collapsing the body removes the variable entirely. @wenshao's A/B measurement (0 height changes vs 3) is the proof, not just the assertion. Widening the existing thought-toggle into an app-wide Both of the maintainer's pre-merge findings are resolved at this head — the M1 test hole is closed (a source-level guard kills the mutation, with the downstream propagation covered behaviourally in MainContent), and the non-VP scrollback redraw is documented exactly as @wenshao offered. The leftover The nits I'd name, none blocking: the Deferring to the maintainer is not warranted: @wenshao has already reviewed the direction, verified the behaviour at runtime, and approved this exact head, so the architectural call a Stage-0 escalation exists to secure has been made. Approving, pinned to the reviewed commit. 中文说明置信度:4/5 —— 干净、净删除代码的改动,采取了严格更简的路径;维护者已深入审查并在本 head 上批准。非阻塞小问题见下。 退一步看:这是对一个真实且已测量问题的正确解法。#7397 的只增不减追踪器一直在与布局对抗——流式期间渲染可变高度内容永远无法靠填充稳定,折叠内容体则彻底移除了该变量。@wenshao 的 A/B 测量(0 次 vs 3 次高度变化)是证据,而非断言。把既有思考切换扩展为应用级 维护者合入前的两项发现均在本 head 解决——M1 测试空洞已闭合(源码级守护击杀该 mutation,下游传播由 MainContent 行为测试覆盖),non-VP scrollback 重绘已按 @wenshao 给出的选项记录在案。残留的 我要点名的小问题(均不阻塞): 无需转交维护者:@wenshao 已审查方向、在运行时验证行为,并批准了这个确切的 head,因此 Stage-0 升级所要确保的架构决策已经做出。批准,固定到受审提交。 — Qwen Code · qwen3.8-max-preview Reviewed at |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
…llDetail toggle The streaming thinking block showed a 4-line preview that varied in height due to empty lines in the model's reasoning output, causing constant page reflow and flicker during generation. Changes: - ThinkBody now renders nothing when collapsed (both streaming and committed), keeping the block at a stable 1-line header height. - Ctrl+O now toggles inline fullDetail mode (like Claude Code): all thinking blocks, tool groups, and tool results expand/collapse in the main conversation view — no alternate-screen overlay. - Alt+T preserved as hidden shortcut (same toggle, not shown in UI). - MainContent passes fullDetail to HistoryItemDisplay via the existing ThoughtExpandedContext, so the toggle works in both VP and Static rendering paths. - Removed TranscriptView overlay rendering, transcriptItems memo, StreamingContext import, and EMPTY_HISTORY_ITEMS constant. - Removed dead code: tailVisualLines, grow-only height tracker, MAX_STREAMING_THINKING_VISUAL_LINES, openTranscript callback.
2084f3b to
3c49353
Compare
|
Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration. 中文请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
已审查。 建议见行内评论。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max-preview via Qwen Code /review
|
@qwen-code /takeover |
|
🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 |
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Address review summary — PR #8077Feedback triageOne inline finding from the automated reviewer:
Decision: Implemented (resolved in code). Verified first: with ChangesIn
Deleted the orphaned component and its tests:
Shared imports ( Conflict notes
Verification
Integration tests were not run: this round is a no-behavior-change dead-code removal (the removed guards could never fire because 中文说明处理审查总结 — PR #8077反馈分类来自自动审查器的一条行内发现:
决定:已实现(在代码中解决)。 先做了验证:随着 变更在
删除了已孤立的组件及其测试:
共享导入( 冲突说明
验证
未运行集成测试:本轮是无行为变化的死代码移除(被移除的守卫永远不会触发,因为 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
已审查。 建议见行内评论。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max-preview via Qwen Code /review
|
🤖 Addressed the latest review feedback (round 2/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 2/100 轮)。改动内容与我反驳保留之处如下: Review feedback addressedrc:3680910174 — Stale keyboard shortcuts help text (Implemented)Finding: Decision: Implement — real user-facing bug, clearly in scope. Changes:
rc:3680910167 — Missing integration test for Ctrl+O → thinking-expansion toggle (Implemented)Finding: The PR deleted five Decision: Implement — valid coverage gap; a future refactor could silently break the keypress → handler → state toggle chain. Changes:
Verification
中文说明已处理的审查反馈rc:3680910174 — 键盘快捷键帮助文本过期(已实现)发现: 决定: 实现——真实的面向用户的 bug,明确在范围内。 变更:
rc:3680910167 — 缺少 Ctrl+O → 思考展开切换的集成测试(已实现)发现: 本 PR 删除了 5 个 决定: 实现——有效的覆盖缺口;未来重构可能静默打断 按键 → handler → 状态切换 的链路。 变更:
验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
评审(基于
|
chiga0
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
已审查。 建议见行内评论。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max-preview via Qwen Code /review
| const { allExpanded: fullDetail } = useThoughtExpanded(); | ||
| const streamingState = uiState.streamingState; |
There was a problem hiding this comment.
[Suggestion] No test covers the MainContent → HistoryItemDisplay fullDetail prop wiring. ToolGroupMessage does not consume useThoughtExpanded context — it relies exclusively on the fullDetail prop. If a future refactor drops the four fullDetail={fullDetail} lines below, thought blocks would still expand (via the allExpanded context path in HistoryItemDisplay), masking the regression — but tool groups would stay collapsed on Ctrl+O, and no existing test would turn red. — Concrete cost: silent loss of Ctrl+O tool-group expansion with no test signal.
Consider adding a MainContent-level test that wraps the component in a ThoughtExpandedProvider with allExpanded: true, renders a tool_group history item, and asserts the tool's detailed output is visible.
中文说明
没有测试覆盖 MainContent → HistoryItemDisplay 的 fullDetail prop 传递。ToolGroupMessage 不消费 useThoughtExpanded context——它完全依赖 fullDetail prop。如果未来的重构删除了下方四处 fullDetail={fullDetail},思考块仍会展开(通过 HistoryItemDisplay 中的 allExpanded context 路径),从而掩盖回归——但工具组将在 Ctrl+O 时保持折叠,且没有现有测试会变红。具体代价:Ctrl+O 工具组展开功能静默失效,无测试信号。
建议添加一个 MainContent 级别的测试,用 allExpanded: true 的 ThoughtExpandedProvider 包裹组件,渲染一个 tool_group 历史项,并断言工具的详细输出可见。
— qwen3.8-max-preview via Qwen Code /review
There was a problem hiding this comment.
Valid concern — if the four fullDetail={fullDetail} lines in MainContent were dropped, tool groups would silently lose Ctrl+O expansion with no test signal, since ToolGroupMessage relies exclusively on the prop (not the context). Deferring to a follow-up: the test needs to wrap MainContent in a ThoughtExpandedProvider with allExpanded: true, render a tool_group history item, and assert detailed tool output is visible — a non-trivial addition that this round's scope (docs fix + dead code cleanup) doesn't warrant.
中文说明
关注点有效——如果 MainContent 中四处 fullDetail={fullDetail} 被删除,工具组将静默失去 Ctrl+O 展开功能且无测试信号,因为 ToolGroupMessage 完全依赖该 prop(而非 context)。推迟到后续跟进:该测试需要用 allExpanded: true 的 ThoughtExpandedProvider 包裹 MainContent,渲染一个 tool_group 历史项,并断言工具的详细输出可见——这是一个非平凡的增量,本轮范围(文档修复 + 死代码清理)不适合包含。
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
已审查。 建议见行内评论。 1 条建议级发现无法锚定到改动行,已丢弃;此处无需进一步处理。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max-preview via Qwen Code /review
| const writesBefore = mockStdout.write.mock.calls.length; | ||
| act(() => { | ||
| handleKeypress!(ctrlO); | ||
| }); | ||
| expect(mockStdout.write.mock.calls.length).toBeGreaterThan(writesBefore); |
There was a problem hiding this comment.
[Suggestion] This replacement for the five deleted transcript tests verifies only that Ctrl+O reaches the TOGGLE_THINKING_EXPANDED branch (via the refreshStatic stdout write) — it never asserts that thoughtExpanded actually flipped or that the rendered output changed. — Concrete cost: deleting setThoughtExpanded((prev) => !prev) from the handler (AppContainer.tsx:3655-3658) while keeping refreshStatic() leaves this test green, yet Ctrl+O would no longer expand thinking blocks, so that regression ships uncaught. The deleted transcript tests all asserted visible output via lastFrame().
Suggested fix — assert on the rendered frame in addition to the stdout write, e.g. render a gemini_thought history item, then:
act(() => { handleKeypress!(ctrlO); });
expect(lastFrame()).toContain('<the full thinking text>');
act(() => { handleKeypress!(ctrlO); });
expect(lastFrame()).not.toContain('<the full thinking text>');中文说明
这个用于替代被删除的 5 个 transcript 测试的新测试,仅验证了 Ctrl+O 到达 TOGGLE_THINKING_EXPANDED 分支(通过 refreshStatic 的 stdout 写入),从未断言 thoughtExpanded 状态确实翻转,也未断言渲染输出发生变化。— 具体代价:从 handler(AppContainer.tsx:3655-3658)中删除 setThoughtExpanded((prev) => !prev) 而保留 refreshStatic(),此测试仍会通过,但 Ctrl+O 将不再展开思考块,该回归会漏网。被删除的 transcript 测试都通过 lastFrame() 断言了可见输出。
建议修复 —— 除 stdout 写入外,再断言渲染帧(渲染一个 gemini_thought history item,按 Ctrl+O 后断言完整思考文本出现,再按一次后断言其消失)。
— qwen3.8-max-preview via Qwen Code /review
|
🤖 Addressed the latest review feedback (round 3/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 3/100 轮)。改动内容与我反驳保留之处如下: Round summaryAddressed review feedback from @wenshao's detailed review and the automated reviewer's inline suggestion. Feedback points and decisions#1 — User docs still describe deleted transcript (Required) #1b — Help text understates behavior (Suggestion → Implemented) #2 — Static rendering path unbounded output (Escalated — maintainer decision) #3 — VP height estimation doesn't account for fullDetail (Declined — follow-up) #4 — Lost error boundary for fullDetail rendering (Declined — follow-up) #5 — Dead code cleanup (Required)
#6 — Duplicate expansion signal, inconsistent scope (Declined — follow-up) #7 — Naming and stale comments (Partially implemented) #8 — Pending area silently clips full detail (Declined — follow-up) UX tradeoff — streaming preview removal (Escalated — product decision) Inline [rc:3681742077] — MainContent-level test for fullDetail prop wiring (Declined — scope) Test coverage suggestions (Declined — follow-up) Conflict notesNo conflicts ( Verification
中文说明本轮总结处理了 @wenshao 的详细审查和自动审查器的行内建议。 反馈要点及决定#1 — 用户文档仍在描述已删除的 transcript(必须修复) #1b — 帮助文案低估了实际行为(建议 → 已实施) #2 — 静态渲染路径输出无上界(升级 — 等待维护者决策) #3 — VP 高度估算未考虑 fullDetail(拒绝 — 后续跟进) #4 — fullDetail 渲染丢失错误边界(拒绝 — 后续跟进) #5 — 死代码清理(必须修复)
#6 — 展开信号重复,各视图作用域不一致(拒绝 — 后续跟进) #7 — 命名和注释已与行为脱节(部分实施) #8 — pending 区域静默裁剪 full detail(拒绝 — 后续跟进) UX 权衡 — 流式预览移除(升级 — 产品决策) 行内 [rc:3681742077] — MainContent 级别的 fullDetail prop 传递测试(拒绝 — 范围) 测试覆盖建议(拒绝 — 后续跟进) 冲突说明无冲突( 验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max-preview via Qwen Code /review
| mockStdout.write.mockClear(); | ||
| handleKeypress!(ctrlO); | ||
| expect(mockStdout.write).toHaveBeenCalledWith(ansiEscapes.clearTerminal); |
There was a problem hiding this comment.
[Critical] This test is named "Ctrl+O flips the full-detail state" but never asserts the state flips. Its only post-keypress assertion is expect(mockStdout.write).toHaveBeenCalledWith(ansiEscapes.clearTerminal) — a side effect of refreshStatic() that runs whether or not setThoughtExpanded is called. capturedThoughtExpanded.allExpanded is asserted only before the keypress (toBe(false)), never after. — Failure scenario: remove setThoughtExpanded((prev) => !prev) from the TOGGLE_THINKING_EXPANDED case in AppContainer.tsx (~line 3669) and Ctrl+O becomes a silent no-op (terminal clears, nothing expands) while this test and the full suite stay green. Mutation probe at HEAD a32dfecf confirms it: commenting out that line leaves AppContainer.test.tsx 134/134 and AppContainer + MainContent 153/153 passing. This is maintainer @wenshao's mutation M1 (comment 5143375337), made an explicit merge condition ("whatever test you add must fail when the flip is removed"). The in-code comment claims the MainContent integration test verifies propagation, but that test injects allExpanded directly via the provider, so it cannot catch a broken AppContainer→context link.
Fix: assert the flip through a channel that reflects it. The obvious expect(capturedThoughtExpanded.allExpanded).toBe(true) does NOT work under this vi.mock('ink') harness (the capture stays false even with the flip present) — assert what MainContent forwards as fullDetail, render a context consumer with proper act flushing, or assert the rendered expansion behavior. Acceptance bar (M1): the assertion must fail when setThoughtExpanded((prev) => !prev) is removed.
中文说明
[Critical] 这个测试名为 "Ctrl+O flips the full-detail state",但从未断言状态真的翻转。它在按键之后唯一的断言是 expect(mockStdout.write).toHaveBeenCalledWith(ansiEscapes.clearTerminal) —— 这是 refreshStatic() 的副作用,无论是否调用 setThoughtExpanded 都会执行。capturedThoughtExpanded.allExpanded 只在按键之前被断言(toBe(false)),按键之后从未再检查。— 失败场景:从 AppContainer.tsx(约 3669 行)的 TOGGLE_THINKING_EXPANDED 分支中删掉 setThoughtExpanded((prev) => !prev),Ctrl+O 就会变成一个静默的空操作(终端被清空、但什么都不展开),而本测试和整个测试套件仍然全绿。在 HEAD a32dfecf 上的变异探针已证实:注释掉该行后,AppContainer.test.tsx 仍 134/134 通过,AppContainer + MainContent 仍 153/153 通过。这正是维护者 @wenshao 的变异 M1(评论 5143375337),并被明确列为合入条件("你新加的测试必须在移除状态翻转后失败")。代码内注释声称 MainContent 集成测试会验证传播,但那个测试是通过 provider 直接注入 allExpanded 的,因此无法捕获 AppContainer→context 这条链路的断裂。
修复建议:通过一个真正能反映状态变化的通道来断言翻转。注意最直观的 expect(capturedThoughtExpanded.allExpanded).toBe(true) 在这套 vi.mock('ink') 下并不可行(即使翻转存在,捕获值也始终为 false)——应断言 MainContent 转发出去的 fullDetail、用正确的 act flush 渲染一个 context 消费者、或断言渲染出的展开行为。验收标准(M1):新断言必须在移除 setThoughtExpanded((prev) => !prev) 后失败。
— qwen3.8-max-preview via Qwen Code /review
|
🤖 AutoFix ran out of time before finishing (timeout (3000000ms)) (attempt 2/100) — it will retry on the next scan. What I found before stopping: See the Qwen Autofix agent step logs for model/tool output. Run log: https://github.com/QwenLM/qwen-code/actions/runs/30646547838 🧠 Handled by Qwen Code · model/模型 |
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Unresolved, please confirm: [Critical] Non-VP full-detail scrollback dump (packages/cli/src/ui/components/messages/ToolGroupMessage.tsx:459 + MainContent non-VP path): with ui.useTerminalBuffer=false, Ctrl+O full-detail sets availableTerminalHeightPerToolMessage=undefined + forceShowResult, so refreshStatic() re-emits the ENTIRE history with full untruncated tool output into terminal scrollback (repeating on collapse). Mechanism verified present at HEAD 7a80a28. Maintainer @wenshao ruled this NOT a merge blocker (comment 5143375337 — pre-existing wipe, fallback path, bounded by conversation length) but asked the author to record the decision: gate the height-cap lift on VP mode, or document the redraw in docs/users/reference/keyboard-shortcuts.md. Whether that documentation was added is not verifiable from the code. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
已审查。 未决,请确认:[Critical] Non-VP full-detail scrollback dump (packages/cli/src/ui/components/messages/ToolGroupMessage.tsx:459 + MainContent non-VP path): with ui.useTerminalBuffer=false, Ctrl+O full-detail sets availableTerminalHeightPerToolMessage=undefined + forceShowResult, so refreshStatic() re-emits the ENTIRE history with full untruncated tool output into terminal scrollback (repeating on collapse). Mechanism verified present at HEAD 7a80a28. Maintainer @wenshao ruled this NOT a merge blocker (comment 5143375337 — pre-existing wipe, fallback path, bounded by conversation length) but asked the author to record the decision: gate the height-cap lift on VP mode, or document the redraw in docs/users/reference/keyboard-shortcuts.md. Whether that documentation was added is not verifiable from the code. 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max-preview via Qwen Code /review
The existing test only asserted clearTerminal (a refreshStatic side
effect) and never verified the setThoughtExpanded state flip. Under
the vi.mock('ink') harness the mocked App never re-renders from a
directly-called handler, so a behavioural allExpanded assertion is
not possible. Add a structural guard on the handler source that
fails when setThoughtExpanded is removed (mutation M1 verified).
|
🤖 Addressed the latest review feedback (round 3/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 3/100 轮)。改动内容与我反驳保留之处如下: Round summaryInline findings[rc:3691987698] Critical — Ctrl+O test has no assertion on the state flip (M1) Decision: Resolved. The existing test only asserted Fix: Added a structural assertion M1 verified: commenting out Issue-level findings (from @wenshao's local verification)Finding 1 — Ctrl+O test teeth: Same as the inline Critical above; resolved by the same commit. Finding 2 — non-VP mode scrollback volume: Escalated for maintainer decision. @wenshao explicitly marked this "maintainer call — this is mine to make" and offered two options: (a) gate the height-cap lift on VP mode, or (b) keep as-is and document in Finding 3 — unused Review-level findingCHANGES_REQUESTED — Integration Tests skipped in CI: This is a CI configuration/scheduling issue, not a code defect. The integration test suite was skipped (not failed) in the CI run. No code change is applicable. Verification
中文说明本轮总结行内评审意见[rc:3691987698] Critical — Ctrl+O 测试未对状态翻转做任何断言(M1) 决定:已修复。 原有测试仅断言 修复方式: 在现有测试中新增结构性断言 M1 已验证: 注释掉 Issue 级发现(来自 @wenshao 的本地验证)发现 1 — Ctrl+O 测试咬合力: 与上述行内 Critical 相同,由同一提交修复。 发现 2 — non-VP 模式 scrollback 数据量: 已升级为维护者决策。@wenshao 明确标注"维护者决策,这条由我来定",并给出两个选项:(a) 将解除高度上限的行为限定在 VP 模式;(b) 保持现状并在 发现 3 — 未使用的 Review 级发现CHANGES_REQUESTED — 集成测试在 CI 中被跳过: 这是 CI 配置/调度问题,非代码缺陷。集成测试套件在该次 CI 运行中被跳过(skip),而非失败(fail)。无需代码变更。 验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
中文说明
已审查——无阻断问题。 建议见行内评论。
— qwen3.8-max-preview via Qwen Code /review
| // this source-level guard closes the remaining gap: removing | ||
| // setThoughtExpanded((prev) => !prev) from AppContainer makes | ||
| // this assertion fail (mutation M1). | ||
| expect(handleKeypress!.toString()).toContain('setThoughtExpanded'); |
There was a problem hiding this comment.
[Suggestion] The .toString() structural assertion confirms setThoughtExpanded appears in the handler source but cannot distinguish (prev) => !prev (correct toggle) from (prev) => true (always expand, never collapse) or (prev) => prev (no-op). — Concrete cost: a mutation to setThoughtExpanded(true) would leave this test, the MainContent wiring tests, and the keyMatchers tests all green while Ctrl+O expands but never collapses — the user is stuck in expanded mode with no toggle-off. The test name ("Ctrl+O flips the full-detail state") claims toggle coverage it cannot deliver.
| expect(handleKeypress!.toString()).toContain('setThoughtExpanded'); | |
| expect(handleKeypress!.toString()).toMatch(/setThoughtExpanded\(\s*\(prev\)\s*=>\s*!prev/); |
中文说明
[Suggestion] .toString() 结构断言只能确认 handler 源码中包含 setThoughtExpanded,但无法区分 (prev) => !prev(正确的切换)与 (prev) => true(始终展开、永不折叠)或 (prev) => prev(空操作)。— 具体代价:若将更新器变异为 setThoughtExpanded(true),本测试、MainContent 接线测试和 keyMatchers 测试将全部保持绿色,而 Ctrl+O 只能展开不能折叠——用户将被困在展开模式中无法切换回来。测试名称("Ctrl+O flips the full-detail state")声称具有它实际无法提供的切换覆盖。
— qwen3.8-max-preview via Qwen Code /review
…aw (#8077) Tighten the structural M1 assertion from .toContain to a regex that matches the (prev) => !prev updater pattern, catching mutations like (prev) => true that the old check would miss. Document the non-VP scrollback redraw in keyboard-shortcuts.md per maintainer request.
|
🤖 Addressed the latest review feedback (round 4/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 4/100 轮)。改动内容与我反驳保留之处如下: Round summaryReview-level: Non-VP full-detail scrollback dump (Critical, ruled NOT a blocker by @wenshao)Decision: Addressed — documented the redraw behavior. The maintainer ruled this pre-existing behavior is not a merge blocker (fallback path, bounded by conversation length) and asked the author to either gate the height-cap lift on VP mode or document the redraw. Added a sentence to the Inline [rc:3692952746]: Strengthen
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
已审查。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max-preview via Qwen Code /review
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Autofix review round — no action takenThis round's feedback contains a single item: a Classification
CI evidence (this PR head)
Because the feedback raises no defect and requests no change, no commit is made 中文说明Autofix 审查轮次 — 未采取任何操作本轮反馈仅包含一条:来自自动审查机器人( 分类
CI 证据(本 PR 的 head)
由于该反馈没有提出任何缺陷,也没有请求任何修改,本轮不提交任何 commit。 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
Local verification report — real build, real terminal, real modelVerified this PR end-to-end on a locally built binary driven through
Verdict: the PR does what it claims, in both render modes. No regression found. Five non-blocking nits below. 1. Build and static checks
2. Automated tests
3. Runtime evidence3.1 The stated bug is fixed — measured, not eyeballedSame prompt, same 120×40 terminal, frames captured every 150 ms during the thinking phase, on both builds:
The left/middle/right panels are the same run at t≈7 s / 10 s / 16 s. On 3.2 Ctrl+O expands in place, and expansion is real full detailConfirmed in a real session, in this order: collapsed →
3.3 The full-screen overlay is genuinely goneObjective signal, read from tmux rather than from pixels —
On 3.4 Robustness cases (not in the PR's test plan)
4. Merge readiness
5. Non-blocking observations
Reproductiongit worktree add ../wt-pr8077 pr-8077 && cd ../wt-pr8077
npm ci && npm run build
npm run typecheck && npm run lint:ci && npm run check-i18n
npx vitest run --root packages/cli \
src/ui/AppContainer.test.tsx src/ui/components/MainContent.test.tsx \
src/ui/keyMatchers.test.ts src/ui/utils/textUtils.test.ts \
src/ui/components/messages/ConversationMessages.test.tsx \
src/ui/components/messages/ToolGroupMessage.test.tsx
# real session, scripted through tmux (repeat against a `main` worktree to compare)
tmux new-session -d -s v -x 120 -y 40 -c "$WS" "node --expose-gc $PWD/packages/cli"
tmux send-keys -t v -l "<prompt>"; tmux send-keys -t v Enter
# sample frames during the thinking phase
for i in $(seq 1 200); do tmux capture-pane -p -e -t v > frame-$i.ansi; sleep 0.15; done
tmux send-keys -t v C-o # toggle full detail
tmux display -p -t v '#{alternate_on}' # 0 on this PR, 1 on main (useTerminalBuffer=false)中文版报告本地验证报告 —— 真实构建、真实终端、真实模型在本地构建的产物上通过
结论:PR 声称的行为在两种渲染模式下均成立,未发现回归。另有 5 条不阻塞合并的小问题。 1. 构建与静态检查
2. 自动化测试
3. 运行时证据3.1 目标缺陷确实修好了 —— 是实测,不是目测同一 prompt、同一 120×40 终端,在思考阶段每 150 ms 抓一帧,两个构建对比:
截图见英文版第一张:左/中/右为同一次运行的 t≈7s / 10s / 16s。 3.2 Ctrl+O 就地展开,且展开的是真正的全详情按 折叠 →
3.3 全屏覆盖层确实被移除了客观信号取自 tmux 而非肉眼 —— pane 的
3.4 健壮性用例(PR 测试计划之外)
4. 合并就绪度
5. 不阻塞合并的观察
|
|
@qwen-code /triage |
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 28 passed · 0 failed · 28 total 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:28 通过 · 0 失败 · 28 总计 Verification report (report.md)Evidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM — verified against this commit: the flicker fix and the inline Ctrl+O full-detail toggle are clean, the maintainer's two pre-merge findings (M1 test hole, non-VP redraw doc) are both resolved, and CI is green. Looks ready to ship. ✅
Resolve the AppContainer message-queue drain conflict by keeping the useQueuedSubmissionDrain hook introduced on this branch and adopting main's removal of the transcript overlay (QwenLM#8077): drop the now-obsolete isTranscriptOpen guard from the hook, its call site, and the hook tests, since the transcript is an inline Ctrl+O toggle that no longer blocks queue draining.
|
Released in v0.21.3. |






What this PR does
Hides the streaming thinking preview by default so the block stays at a constant 1-line header during generation, eliminating page reflow flicker. Replaces the full-screen transcript overlay (old Ctrl+O) with an inline full-detail toggle that expands all thinking blocks, tool groups, and tool results in-place — matching Claude Code's Ctrl+O behavior.
Why it's needed
The streaming thinking block rendered a 4-line tail preview whose height varied (1–5 visual rows) as the model's reasoning streamed in. Empty lines and list markers in the output caused the block to jump between heights on every chunk, pushing all content below it up and down continuously. The grow-only height tracker mitigated but did not eliminate this — the fundamental problem is that variable-height content cannot be stabilized by padding alone.
The old Ctrl+O opened a separate alternate-screen overlay (TranscriptView) that required a context switch and lost the user's scroll position. Users expect Ctrl+O to toggle detail level in-place, like Claude Code.
Reviewer Test Plan
How to verify
enable_thinking).∴ Thinking… Xs— no body content, height constant at 1 line regardless of how long the model thinks.Evidence (Before & After)
Before (from issue screenshots): Thinking block height jumps between 2–6 rows as content streams. Ctrl+O opens a separate full-screen overlay.
After (tmux capture, macOS, 120×40,
npm run dev):Collapsed state (default) — thinking block is exactly 1 line, hint shows
ctrl+o:After pressing Ctrl+O — thinking expands inline, hint changes to
ctrl+o to collapse:After pressing Ctrl+O again — collapses back to 1-line header:
Key observations:
Thinking… Xsphase, but the collapsed header is always 1 line)ctrl+ohint text displayed correctly (notalt+toroption+t)Tested on
Environment
npm run dev(local TypeScript source, no build needed), tmux 120×40Risk & Scope
AgentChatContent) does not yet receive thefullDetailprop — Ctrl+O expands thinking blocks there via context but not tool groups. Follow-up PR.Linked Issues
N/A
中文说明
本 PR 做了什么
默认隐藏流式思考预览,使思考块在生成期间保持恒定的 1 行标题高度,消除页面重排闪烁。将全屏 transcript 覆盖层(旧 Ctrl+O)替换为 inline 全详情切换,在主对话视图中原地展开所有思考块、工具组和工具结果——与 Claude Code 的 Ctrl+O 行为一致。
为什么需要
流式思考块渲染了一个 4 行尾部预览,其高度随模型推理内容的流入而变化(1-5 个视觉行)。输出中的空行和列表标记导致块在每个 chunk 之间跳动高度,持续推动下方所有内容上下移动。grow-only 高度追踪器缓解但未消除此问题——根本问题是可变高度的内容无法仅通过填充来稳定。
旧的 Ctrl+O 打开一个单独的交替屏幕覆盖层(TranscriptView),需要上下文切换并丢失用户的滚动位置。用户期望 Ctrl+O 能像 Claude Code 一样原地切换详情级别。
风险与范围